#include "amever.h"
#include "lua_w.h"

int luaw_init(void) {
 s.L = lua_open();
 luaopen_base(s.L);
 luaopen_table(s.L);
 luaopen_string(s.L);
 luaopen_io(s.L);
 luaopen_math(s.L);
 luaopen_debug(s.L);
 luaw_registerFunctions (s.L);
 amelog("Lua wrapper inited.\n");
}

void luaw_shut(void) {
	amelog("Closing lua wrapper.\n");
	if (s.L) {
		lua_close(s.L);
	}
}

/* LUA Wrapping funx */
struct luaw_functionTable {
	enum {
		_luaw_testFunction,
		_luaw_amelog,
		_luaw_printError,
		_luaw_sendMsgToAllClients,
		_luaw_mapAddObject,
		_luaw_lastFunction
	} id;
	char name[16];
	lua_CFunction function;
};

LUAW_FUNCTION(testFunction);
LUAW_FUNCTION(amelog);
LUAW_FUNCTION(printError);
LUAW_FUNCTION(sendMsgToAllClients);
LUAW_FUNCTION(mapAddObject);

static struct luaw_functionTable luaw_functions[] = {
	{ _luaw_testFunction, "testFunction", luaw_testFunction },
	{ _luaw_amelog, "amelog", luaw_amelog },
	{ _luaw_printError, "printerror", luaw_printError },
	{ _luaw_sendMsgToAllClients, "sendToAll", luaw_sendMsgToAllClients },
	{ _luaw_mapAddObject, "mapAddObject", luaw_mapAddObject },
	{ _luaw_lastFunction, "", NULL }
};

int luaw_registerFunction(lua_State *L, const char *name, lua_CFunction function) {
    lua_pushcfunction(L, function);
    lua_setglobal(L, name);
    amelog("Function \"%s\" sucessfully registred.\n", name);
}

inline void luaw_registerFunctions (lua_State *L) {
	int i;
	for(i=0;i<_luaw_lastFunction;i++) {
		if (luaw_functions[i].function != NULL) {
			luaw_registerFunction(L, luaw_functions[i].name, luaw_functions[i].function);
		}
	}
	amelog("Calling lua/test.lua to make sure is everything olrayts.\n");
	if (!luaL_loadfile(L, "lua/init.lua")) {
		if (lua_pcall(L, 0, LUA_MULTRET, 0))
			amelog("luaError : %s\n", lua_tostring(L, -1));
 	 lua_pop(L, 1); // remove error message

	}
	else {
		amelog("luaError : %s\n", lua_tostring(L, -1));
  lua_pop(L, 1); // remove error message
	}
}

int luaw_testFunction (lua_State *L) {
	lua_getglobal(L, "print");  /* function to be called */
	lua_pushstring(L, "Hellow from lua wrapper.");
 if (lua_pcall(L, 1, 0, 0) != 0)
  amelog("error running function `f': %s",
		lua_tostring(L, -1));

	amelog("C function sucessfully called from lua and viceversa %)\n");

	return 0;
}


int luaw_amelog(lua_State *L) {
	if (lua_gettop(L) > 1) {
		luaw_internalPrintError(L, "wrong count arguments for amelog");
		return 0;
	}
	amelog("%s\n", lua_tostring(L, 1));
	return 0;
}

int luaw_printError (lua_State *L) {
  lua_Debug ar;
  lua_getstack(L, 1, &ar);
  lua_getinfo(L, "nSl", &ar);

		char name[128]; char msg[128];
  if (ar.name == NULL)
			strcpy(name, "global context");
  else
  	strcpy(name, ar.name);

  if (lua_gettop(L) == 1)
			strcpy(msg, lua_tostring(L, 1));
  else
  	strcpy(msg, "");


		amelog("error %s in %s, %s:%d\n", msg, name, ar.source, ar.currentline);
  return 0;
}

int luaw_internalPrintError(lua_State *L, const char *msg) {
  lua_Debug ar;
  lua_getstack(L, 1, &ar);
  lua_getinfo(L, "nSl", &ar);
		char tmp[128];
  if (ar.name == NULL)
			strcpy(tmp, "global context");
  else
  	strcpy(tmp, ar.name);

		amelog("Error: %s in %s on %s:%d\n", msg, ar.what, ar.source, ar.currentline);
}

int luaw_sendMsgToAllClients(lua_State *L) {
	int i;
	for (i=0;i<s.highest_fd;i++) {
		if (s.fds[i].type == FD_CLIENT && CLIENT(i)->logged) {
			WFIFOW(CLIENT(i), 0) = 0;
			WFIFOSP(CLIENT(i), (WFIFOB(CLIENT(i), 2) = sprintf(WFIFOP(CLIENT(i), 3), "Hello from lua")));
		}
	}
}

int luaw_mapAddObject (lua_State *L) {
	/*
	This function will be called from luic scripts to actually add into a map an object
	which can be WO_CHAR, WO_ITEM, WO_MOB or whateva.
	To do this we just push a node into cell->objects and we're done... easy nuf
	function mapAddObject(map_hook, x, y, type)
	returns the object
	*/
	struct world_map *map = (struct world_map *)lua_touserdata(L, 1);
	amelog("Map->mobs is 0x%x\n", map->mobs);
	int x = lua_tonumber(L, 2);
	int y = lua_tonumber(L, 3);
	int type = lua_tonumber(L, 4);
	int id = lua_tonumber(L, 5);
	char name[16];
	strcpy(name, lua_tostring(L, 6));
	struct world_mob *mob = (struct world_mob *)malloc(sizeof(struct world_mob));
	if (!chain_insertUnique(map->mobs, id, mob, 0)) {
		amelog("Warning: Mob with id %d for %s already exists.\n", id, map->name);
		free(mob);
		return 0;
	}
	else {
		amelog("Inited a thing... mobs is %x and mobs->len is %d\n", map->mobs, map->mobs->len);
	}

	if (map->cells[y][x]->objects == 0)
		map->cells[y][x]->objects = chain_setInit(0, 0, MAP_MAX_OBJECTS_IN_CELL);

	struct chain_node *node = 	chain_nodeInit(map->cells[y][x]->objects, 0, type, 0);
	if (map && x <= map->width && y <= map->height) {
		if (type == WO_MOB) {
			memset(mob, 0, sizeof(struct world_mob));
			node->val = mob;
			mob->id = id;
			mob->cell = map->cells[y][x];
			mob->stats.wspeed = 25;
			strcpy(mob->name, name);
		}
		else {
			node->val = 0;
		}
	}

	if (node->val) {
		lua_pushlightuserdata(L, node->val);
	}
	else {
		lua_pushnil(L);
	}
	amelog("Monster %s with id %d inited.\n", mob->name, mob->id);
	return 0;
}
